// -------------------------------------------------
// BlinKit - BlinKit Library
// -------------------------------------------------
//   File Name: runtime_impl.cpp
// Description: RuntimeImpl Class
//      Author: Ziming Li
//     Created: 2022-03-19
// -------------------------------------------------
// Copyright (C) 2022 MingYang Software Technology.
// -------------------------------------------------

#include "./runtime_impl.h"

#include "blinkit/blink/renderer/bindings/core/qjs/qjs.h"
#include "blinkit/blink/renderer/bindings/core/qjs/qjs_bindings.h"
#include "blinkit/blink/renderer/bindings/core/script_wrappable.h"

using namespace blink;
using namespace BlinKit;

RuntimeImpl::RuntimeImpl(void *opaque) : m_runtime(JS_NewRuntime())
{
    JS_SetRuntimeOpaque(m_runtime, opaque);
    ScriptWrappable::Attach(m_runtime);
}

RuntimeImpl::~RuntimeImpl(void)
{
    JS_FreeRuntime(m_runtime);
}

JSContext* RuntimeImpl::CreateContext(unsigned features)
{
    JSContext *ret = JS_NewContext(m_runtime);
    if (0 != features)
    {
        JSValue global = JS_GetGlobalObject(ret);
        if (BK_JS_CONSOLE & features)
            qjs::AddConsole(ret, global);
        if (BK_JS_XML_HTTP_REQUEST & features)
            qjs::AddXMLHttpRequest(ret, global);
        if (BK_JS_BASE64 & features)
            qjs::AddBase64(ret, global);
        JS_FreeValue(ret, global);
    }
    return ret;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C" {

BKEXPORT BkJSRuntime BKAPI BkCreateJSRuntime(void)
{
    return new RuntimeImpl;
}

BKEXPORT void BKAPI BkDestroyJSRuntime(BkJSRuntime rt)
{
    delete rt;
}

BKEXPORT BkJSContext BKAPI BkCreateJSContext(BkJSRuntime rt, unsigned features)
{
    return rt->CreateContext(features);
}

BKEXPORT void BKAPI BkReleaseJSContext(BkJSContext ctx)
{
    JS_FreeContext(ctx);
}

BKEXPORT int BKAPI BkEvaluate(BkJSContext ctx, const char *code, unsigned len, BkBuffer *ret)
{
    int r = BK_ERR_SUCCESS;

    if (0 == len)
        len = strlen(code);

    std::string input(code, len);
    JSValue v = JS_Eval(ctx, input.c_str(), input.length(), "eval", JS_EVAL_TYPE_GLOBAL);
    if (!JS_IsException(v))
    {
        if (!JS_IsUndefined(v))
        {
            std::string output;
            qjs::Context context(*ctx);
            if (context.ToString(output, v))
                BkSetBufferData(ret, output.data(), output.length());
            else
                r = BK_ERR_TYPE;
        }
    }
    else
    {
        r = BK_ERR_EXCEPTION;
    }
    JS_FreeValue(ctx, v);

    return r;
}

} // extern "C"
